/*************头文件***************/
#include <stdlib.h>
#include <NTPClient.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <WiFiManager.h> 
#include<ArduinoJson.h>

//oled
#include <Arduino.h>
#include <U8g2lib.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

/**********************常量定义********************************************/
const char* ssid     = "Hello";             //WIFI名称
const char* password = "1015wl@@";          //WIFI密码
const char* host ="api.seniverse.com";      //服务器地址
const int port = 80;                        //服务器端口号
const char* APIKEY ="S75L7IBJAj4y0UXmZ";    //输入自己申请的知心天气私钥
const char* city ="chongqing";              //可根据需要改为其余城市的拼音
const char* language ="zh-Hans";            //中文--en英文

const unsigned long BAUD_RATE=115200;       //波特率
const size_t MAX_CONTENT_SIZE=1000;         //最大空间
const char endOfHeaders[]="\r\n\r\n";       //响应头结束
char Reply_Packet_Now[MAX_CONTENT_SIZE];    //实时温度缓存区
char response[MAX_CONTENT_SIZE];        //天气预报缓存区

// 建立字符串，用于HTTP请求
String httpRequest =  String("GET /") + " HTTP/1.1\r\n" +
                        "Host: " + host + "\r\n" +
                        "Connection: close\r\n" +
                        "\r\n";


//字库
const unsigned char wen_bits[]=
{
0x00,0x00,0x00,0x00,0x00,0xF0,0x81,0x03,
0x00,0xF8,0x83,0x03,0x00,0xFC,0xE7,0x0F,
0x00,0xFE,0xEF,0x0F,0x00,0xFE,0xEF,0x0F,
0x00,0xFE,0x8F,0x03,0x00,0xFE,0x8F,0x03,
0x00,0xFE,0x0F,0x00,0x00,0x1E,0x0F,0x00,
0x00,0x1E,0x0F,0x00,0x00,0x1E,0x0F,0x00,
0x00,0x1E,0x0F,0x00,0x00,0x1E,0x0F,0x00,
0x00,0x1E,0x0F,0x00,0x00,0x1E,0x0F,0x00,
0x00,0x1E,0x0F,0x00,0x00,0x1E,0x0F,0x00,
0x00,0x1E,0x0F,0x00,0x00,0x1F,0x1F,0x00,
0x80,0x1F,0x3F,0x00,0xC0,0x1F,0x3F,0x00,
0xC0,0x0F,0x7E,0x00,0xC0,0x07,0x7C,0x00,
0xC0,0x07,0x7C,0x00,0xC0,0x07,0x7C,0x00,
0xC0,0x0F,0x7E,0x00,0x80,0xFF,0x7F,0x00,
0x80,0xFF,0x3F,0x00,0x00,0xFF,0x1F,0x00,
0x00,0xFE,0x0F,0x00,0x00,0x00,0x00,0x00,//摄氏度.bmp0
};

const unsigned char du_bits[]={
0x00,0x00,0x00,0x00,0x06,0x0E,0x9F,0x3F,
0xDB,0x71,0xEE,0x70,0xE0,0x00,0xE0,0x00,
0xE0,0x00,0xE0,0x70,0xC0,0x79,0x80,0x3F,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//℃0
};

const unsigned char tian_bits[]={
0x00,0x00,0xFC,0x3F,0x80,0x01,0x80,0x01,
0x80,0x01,0x80,0x01,0xFF,0xFF,0x80,0x01,
0xC0,0x03,0xC0,0x03,0x60,0x06,0x60,0x06,
0x30,0x0C,0x18,0x18,0x0C,0x30,0x07,0xE0,//天
};


const unsigned char qi_bits[]={
0x18,0x00,0x18,0x00,0xFC,0x7F,0x0C,0x00,
0xF6,0x1F,0x03,0x00,0xFC,0x1F,0x00,0x18,
0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,
0x00,0xF0,0x00,0xF0,0x00,0xE0,0x00,0xC0,//气
};

const unsigned char shi_bits[]={
0xC0,0x00,0x80,0x01,0xFE,0xFF,0x06,0xC0,
0x33,0x63,0x60,0x03,0x60,0x03,0x18,0x03,
0x30,0x03,0x30,0x03,0xFF,0xFF,0x80,0x07,
0xC0,0x0C,0x60,0x18,0x38,0x30,0x0E,0x60,//实
};


const unsigned char kuang_bits[]={
0x00,0x00,0xE6,0x3F,0x6C,0x30,0x6C,0x30,
0x60,0x30,0x60,0x30,0xF8,0x3F,0x98,0x0D,
0x8C,0x0D,0x8F,0x0D,0x8C,0x0D,0xCC,0x0C,
0xCC,0xCC,0x6C,0xCC,0x3C,0xF8,0x18,0x00,//况
};

const unsigned char wifi_bits[]={
0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x07,
0xF8,0x1F,0x1C,0x38,0x0E,0x70,0xE0,0x07,
0xF0,0x0F,0x30,0x0C,0xC0,0x03,0x80,0x01,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//wifi.bmp0
};
/************************************************************************/

/******************变量定义*****************************/

char Hours;         // 得到时钟
char Minu;          // 得到分钟
char Sece;          // 得到秒钟
int Time[6];        //时间存放区
int W = 0;          //进度条
int flag = 1;

struct WeatherData{//存储天气数据的结构体，可根据需要自行添加成员变量
  char city[16];
  char Now_weather[32];
  char Now_temp[16];
  char Now_date[32];
  char Now_code[2];
};

struct Time         //储存时间
{
  int Hour_s;
  int Hour_g;
  int Minu_s;
  int Minu_g;
  int Sece_s;
  int Sece_g;
};

/*******************重要对象体***************************/
WiFiUDP ntpUDP;
WiFiManager WIFIconfig;   // 建立WiFiManager对象
WiFiClient client;        // 建立WiFi客户端对象，对象名称client
NTPClient timeClient(ntpUDP, "ntp1.aliyun.com", 60 * 60 * 8, 30 * 60 * 1000);
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0,  D1, D2,  U8X8_PIN_NONE);

struct Time TD;
struct WeatherData WD;
/*******************************************************/


/**********************函数声明区************************/

void OLED_Init();       //OLED初始化
void InitFace();        //欢迎界面
void InitFaceTow();     //加载界面
void ConnectWiFi();     //WIFI连接
void GetTime();         //获取时间
void DisplayTime();     //显示时间
void DisplayMain();     //主界面整体显示

//温度部分
void ClearBuffer();           //清数组缓存
void stopConnect();           //断开连接
bool skipResponseHeaders();   //跳过包头
void GetWeather();            //获取温度--HTTP请求
bool sendRequest_Now(const char* host,const char* cityid,const char* apiKey);   //发送请求
void readReponseContent(char* content,size_t maxSize);                          //读取应答内容
void JsonUserData(char* content,struct WeatherData* weatherData);               //JSON解析
void DisplayTowFace(const struct WeatherData* weatherData);                     //天气实况

void printUserData(const struct WeatherData* weatherData);  //测试串口输出

/********************主程序*****************************/
void setup() 
{
  Serial.begin(BAUD_RATE);    //串口速度 115200
  OLED_Init();                //OLED初始化
  InitFace();                 //欢迎界面
  ConnectWiFi();              //建立WIFI连接
  
  InitFaceTow();              //加载界面
  GetWeather();               //获取第一次温度
  timeClient.begin();
}


void loop() 
{ 
  GetTime();
  ReadWeather();
  SwitchFace();
}

/******************************************************/


/******************************************************
*                  函数定义区                           *
*                                                     *
******************************************************/
// OLED初始化函数
void OLED_Init()
{
  u8g2.begin();           //开启OLED
  u8g2.enableUTF8Print(); //开启UTF8
  u8g2.clearBuffer();     // 清屏
}


// 连接WIFI函数
void ConnectWiFi()    
{
  u8g2.clearBuffer();                     // 清屏
  u8g2.setFont(u8g2_font_ncenB10_tr);     // 选择字型
  u8g2.drawStr(5,15,"Connect :");         // 写数据
  u8g2.drawStr(80,15,ssid);               //显示WIFI名
  u8g2.sendBuffer();                      // 将内存传输到显示器
  delay(2000);
  u8g2.clearBuffer();                     // 清屏
  u8g2.setFont(u8g2_font_ncenB10_tr);     // 选择字型
  u8g2.drawStr(40,40,"Wait...");          // 写数据
  u8g2.sendBuffer();                      // 将内存传输到显示器
  
  WiFi.begin(ssid, password);             // 启动网络连接

  while (WiFi.status() != WL_CONNECTED)   // 如果WiFi连接成功则返回值为WL_CONNECTED 
  {                                       // WiFi.status()函数的返回值是由NodeMCU的WiFi连接状态所决定的。 
    u8g2.drawBox(0,55,W+=6,6);            //（起始X，起始Y，方形的宽W，方形的高H）
    u8g2.sendBuffer();                    // 将内存传输到显示器
    delay(800);

    //如果20s还没连接上，就进行手动配网
    if(W >= 120)
    {
      u8g2.clearBuffer();                   // 清屏
      u8g2.setFont(u8g2_font_ncenB10_tr);   // 选择字型
      u8g2.drawStr(10,15,"Place Connect");  // 显示用户配网WIFI
      u8g2.drawStr(10,40,"ESP8266_WIFI");
      u8g2.sendBuffer();                    // 将内存传输到显示器
      
      WIFIconfig.resetSettings();               // 清除ESP8266所存储的WiFi连接信息
      WIFIconfig.autoConnect("ESP8266_WIFI");   // 创建WIFI信息
      WiFi.persistent(true);                    // 保存WIFI信息    
    }   
  }
  u8g2.clearBuffer();                           // 清屏
  u8g2.setFont(u8g2_font_ncenB10_tr);           // 选择字型
  u8g2.drawStr(0,30,"Connect Success");         // 显示连接成功
  u8g2.drawStr(5,60,"IP:");                     
  u8g2.setCursor(25,60);                        // 设置光标位置
  u8g2.print(WiFi.localIP());                   // 显示配网IP
  u8g2.sendBuffer();                            // 将内存传输到显示器    
  delay(2000);
}

//获取时间
void GetTime()
{
  timeClient.update();//更新时间                                              
  TD.Hour_s = (timeClient.getHours())/10;     // 小时十位
  TD.Hour_g = (timeClient.getHours())%10;     // 小时个位
  
  TD.Minu_s = (timeClient.getMinutes())/10;   // 分钟十位
  TD.Minu_g = (timeClient.getMinutes())%10;   // 分钟个位
  
  TD.Sece_s = (timeClient.getSeconds())/10;
  TD.Sece_g = (timeClient.getSeconds())%10;
}

//http请求
void GetWeather()
{
  client.connect(host,80);          //建立连接                       
  while(!client.connected());       //等待连接，连接失败-0，成功-1 

  if(sendRequest_Now(host,city,APIKEY)&&client.find(endOfHeaders))//发送GET请求，并且跳过响应头找到文尾
  {
    ClearBuffer();                                                //清除缓存
    readReponseContent(Reply_Packet_Now,sizeof(Reply_Packet_Now));//读取返回值，并计算大小
    JsonUserData(Reply_Packet_Now,&WD);                           //JSON解析数据，并存入天气结构体数组
  }
  stopConnect();
}

//获取实时天气
bool sendRequest_Now(const char* host,const char* cityid,const char* apiKey)
{
  String GetUrl="/v3/weather/now.json?key=";
  GetUrl+=APIKEY;
  GetUrl+="&location=";
  GetUrl+=city;
  GetUrl+="&language=";
  GetUrl+=language;
  GetUrl+="&unit=c ";
  client.print(String("GET ")+GetUrl+"HTTP/1.1\r\n"+"Host:"+host+"\r\n"+"Connection:close\r\n\r\n");
  Serial.println("creat a request:");
  Serial.println(String("GET ")+GetUrl+"HTTP/1.1\r\n"+"Host:"+host+"\r\n"+"Connection:close\r\n\r\n");
  delay(1000);
  return true;
}

//读取应答内容
void readReponseContent(char* content,size_t maxSize)
{
  size_t SizeDate = client.readBytes(content,maxSize);
  delay(100);
  content[SizeDate] = 0;
  Serial.println(content);//输出读取到的数据
  client.flush();//刷新客户端
}


//JSON解析
void JsonUserData(char* content,struct WeatherData* weatherData){
  DynamicJsonBuffer jsonBuffer;//创建一个动态缓冲区实例
  JsonObject&root=jsonBuffer.parseObject(content);//根据需要解析的数据来计算缓冲区的大小
  
  //复制数据包中所需的字符串
  strcpy(weatherData->city,root["results"][0]["location"]["name"]);         //城市
  strcpy(weatherData->Now_weather,root["results"][0]["now"]["text"]);       //天气状况
  strcpy(weatherData->Now_temp,root["results"][0]["now"]["temperature"]);   //温度
  strcpy(weatherData->Now_date,root["results"][0]["last_update"]);          //更新时间
  strcpy(weatherData->Now_code,root["results"][0]["now"]["code"]);          //天气代码
}

//测试数据
void printUserData(const struct WeatherData* weatherData){
  Serial.println("数据:");
  Serial.print("城市:");
  Serial.print(weatherData->city);
  Serial.print("  天气:");
  Serial.print(weatherData->Now_weather);
  Serial.print("  温度:");
  Serial.print(weatherData->Now_temp[0]);
  Serial.print("℃");
  Serial.print(weatherData->Now_temp[1]);
  Serial.print("℃");
  Serial.print("  时间:");
  Serial.print(weatherData->Now_date);
  Serial.print("  天气代码：");
  Serial.println(weatherData->Now_code);
}

//跳过响应头
bool skipResponseHeaders()
{
  bool ok=client.find(endOfHeaders);
  if(!ok){
    Serial.println("无响应！");
  }
  return ok;
}

//断开连接
void stopConnect()
{
  Serial.println("断开连接！");
  client.stop();//停止客户端访问
}

//清除数组缓存
void ClearBuffer()
{
  memset(response,0,MAX_CONTENT_SIZE);
}

//定时10分钟获取一次温度
void ReadWeather()
{
  if(TD.Minu_g == 0&&flag == 1)
  {
    flag = 0;
    GetWeather();
  }else if(TD.Minu_g != 0){
    flag = 1;
  }
}
//-------------------- 显示函数区 ----------------------------
//欢迎界面
void InitFace()
{
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_ncenB14_tr);
  u8g2.setCursor(20, 40);
  u8g2.print("Welcome");
  u8g2.sendBuffer();
  delay(2000);
}

//加载界面
void InitFaceTow()
{
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_ncenB14_tr);
  u8g2.setCursor(14, 40);
  u8g2.print("Loading");
  u8g2.setCursor(94, 38);
  u8g2.print(". . .");
  u8g2.sendBuffer();
}

//顶部图标显示
void DisplayHead()
{
  u8g2.drawXBM(0,0,16,16,wifi_bits);
  u8g2.setFont(u8g2_font_unifont_t_symbols);
  if(timeClient.getSeconds()%2==0)
  {
    u8g2.drawGlyph(120, 12, 0x21e7);               //上箭头图标
  }else{
    u8g2.drawGlyph(120, 12, 0x21e9);               //下箭头图标
  }
}
//主界面时间显示
void DisplayTime()
{
  u8g2.setFont(u8g2_font_ncenB24_tr);
  u8g2.setCursor(0,40);
  u8g2.print(TD.Hour_s); u8g2.print(TD.Hour_g);  //时
  u8g2.setCursor(40,37);
  u8g2.print(":");  
  u8g2.setCursor(50, 40);
  u8g2.print(TD.Minu_s); u8g2.print(TD.Minu_g);  //分
  u8g2.setCursor(90, 37);
  u8g2.print(":");
  u8g2.setFont(u8g2_font_ncenB18_tr);
  u8g2.setCursor(101, 38);
  u8g2.print(TD.Sece_s); u8g2.print(TD.Sece_g);  //分
}

//主界面日期显示
void DisplayDate(const struct WeatherData* weatherData)
{
  u8g2.drawHLine(0,45,128);
  u8g2.setFont(u8g2_font_ncenB12_tr);
  u8g2.setCursor(15, 60);
  u8g2.print(weatherData->Now_date[0]);
  u8g2.setCursor(25, 60);
  u8g2.print(weatherData->Now_date[1]);
  u8g2.setCursor(35, 60);
  u8g2.print(weatherData->Now_date[2]);
  u8g2.setCursor(45, 60);
  u8g2.print(weatherData->Now_date[3]);
  u8g2.setCursor(60, 60);
  u8g2.print("/");

  u8g2.setCursor(65, 60);
  u8g2.print(weatherData->Now_date[5]);
  u8g2.setCursor(75, 60);
  u8g2.print(weatherData->Now_date[6]);
  u8g2.setCursor(90, 60);
  u8g2.print("/");

  u8g2.setCursor(95, 60);
  u8g2.print(weatherData->Now_date[8]);
  u8g2.setCursor(105, 60);
  u8g2.print(weatherData->Now_date[9]);
}

//主界面整体显示
void DisplayMain() 
{
  DisplayHead();
  DisplayTime();
  DisplayDate(&WD);
}

//天气界面
void DisplayTowFace(const struct WeatherData* weatherData)
{
  u8g2.drawXBM(30,0,16,16,tian_bits);
  u8g2.drawXBM(50,0,16,16,qi_bits);
  u8g2.drawXBM(70,0,16,16,shi_bits);
  u8g2.drawXBM(90                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  ,0,16,16,kuang_bits);
 
  u8g2.drawHLine(0,16,128);
  u8g2.drawVLine(76,16,60);

  u8g2.drawXBM(4,18,16,16,du_bits);
  u8g2.drawXBM(0,32,32,32,wen_bits);

  if(weatherData->Now_temp[1] == NULL)
  {
    u8g2.setFont(u8g2_font_ncenB24_tr);
    u8g2.setCursor(40,55);
    u8g2.print(weatherData->Now_temp);
  }else{
    u8g2.setFont(u8g2_font_ncenB24_tr);
    u8g2.setCursor(32,55);
    u8g2.print(weatherData->Now_temp);
  }
  
//天气图标
  if(weatherData->Now_code[0] != NULL&&weatherData->Now_code[1] != NULL)
  {
    switch(weatherData->Now_code[1])
    {  
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
            u8g2.setFont(u8g2_font_open_iconic_weather_4x_t); // 选择字型
            u8g2.drawGlyph(92,60,0x0043); //小雨
            break;
    }
  }
  else
  {
    switch(weatherData->Now_code[0])
    {
      case '0':
            u8g2.setFont(u8g2_font_open_iconic_weather_4x_t); // 选择字型
            u8g2.drawGlyph(92,60,0x0045); //晴天
            break;
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
            u8g2.setFont(u8g2_font_open_iconic_weather_4x_t); // 选择字型
            u8g2.drawGlyph(92,60,0x0041); //多云
            break;
      case '9':
            u8g2.setFont(u8g2_font_open_iconic_weather_4x_t); // 选择字型
            u8g2.drawGlyph(92,60,0x0040); //阴
            break;
    } 
  } 
}

//页面选择
void SwitchFace()
{
  u8g2.clearBuffer();
  if(timeClient.getSeconds() <= 10||(timeClient.getSeconds() > 30&&timeClient.getSeconds() <= 40))
  {
      DisplayHead();      
      DisplayTowFace(&WD); 
  }else{
      DisplayMain();
  }
  u8g2.sendBuffer();
}
